<template>
  <view
    class="x-tree_mask"
    :class="show ? 'tree-show' : ''"
    @click.stop="clickMask"
    :style="{ zIndex: zIndex }"
  ></view>
  <view
    class="x-tree_container"
    :class="show ? 'tree-show' : ''"
    :style="{ zIndex: zIndex + 1 }"
  >
    <view class="x-tree_top">
      <view class="x-tree_cancel-btn x-tree_btn" @click.stop="close">取消</view>
      <view class="x-tree_title">{{ title }}</view>
      <view class="x-tree_btn" style="display: flex; align-items: center">
        <view class="x-tree_clear-btn" @click.stop="clear">清空</view>
        <view class="x-tree_v-divider"></view>
        <view class="x-tree_confirm-btn" @click.stop="confirm">确定</view>
      </view>
    </view>
    <view v-if="showSearch" class="x-tree_top-search">
      <input v-model="searchValue" class="x-tree_search-input" placeholder="请输入"/>
    </view>
    <view class="x-tree_content">
      <!-- 滑动到底部时，不加控制，会触发底下的页面滚动 -->
      <scroll-view class="x-tree_list" :scroll-y="true" :show-scrollbar="false">
        <view
          v-for="(item, index) in treeData"
          :key="item.id"
          class="x-tree_item"
          :style="{ paddingLeft: item.level * 20 + 'rpx' }"
          :class="[item.isShowSelf ? 'x-tree_item-show' : 'x-tree_item-hide']"
        >
          <!-- item-icon 用来撑开位置，要不要显示内容，看内部实现 -->
          <view
            @click.stop="toggleItem(item, index)"
            class="x-tree_item-icon"
            :class="[
              item.isShowChild
                ? 'x-tree_item-icon-arrow-show'
                : 'x-tree_item-icon-arrow-hide',
              item.isLastLevel
                ? 'x-tree_item-icon-last-dot'
                : 'x-tree_item-icon-arrow',
            ]"
          >
          </view>
          <view class="x-tree_item-name" @click.stop="clickName(item, index)"
            > <highlight v-if="showSearch" :search="searchValue" :name="item.name"/>
            <text class="x-tree_item-name-text" v-else>{{ item.name }}</text>
            <text v-if="debug"
              >{{
                "-->" +
                "status:" +
                item.checkStatus +
                "--lv:" +
                item.level +
                "--id:" +
                item.id
              }}
            </text>
          </view>
          <view class="x-tree_item-btn" @click.stop="toggleSelect(item, index)">
            <view
              v-if="!multiple"
              class="x-tree_item-radio"
              :class="item.checkStatus === 1 ? 'x-tree_item-radio-checked' : ''"
            ></view>
            <view
              v-else
              class="x-tree_item-checkbox"
              :class="[
                item.checkStatus === 1
                  ? 'x-tree_item-checkbox-checked'
                  : item.checkStatus === 2
                  ? 'x-tree_item-checkbox-indeterminate'
                  : '',
              ]"
            >
            </view>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script lang="ts" setup>
  import { ref, nextTick, watch } from "vue";
  import highlight from "./highlight.vue";
  const emit = defineEmits(["confirm", "update:visible"]);

  defineOptions({
    name: "x-tree",
  })

  type TreeItemID = string | number;
  type TreeItemValue = TreeItemID[];

  const props = defineProps({
    value: { // 选中值
      type: Array<TreeItemID>,
      default: () => [],
    },
    maskClose: { // 点击蒙版是否关闭
      type: Boolean,
      default: true,
    },
    zIndex: {
      type: Number,
      default: 1000,
    },
    title: { // 顶部文本提示
      type: String,
      default: "",
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    localdata: {
      type: Array,
      default: () => [],
    },
    idField: {
      type: String,
      default: "id",
    },
    nameField: {
      type: String,
      default: "name",
    },
    childrenField: {
      type: String,
      default: "children",
    },
    defaultExpandAll: {
      type: Boolean,
      default: false,
    },
    debug: { // 是否开启日志
      type: Boolean,
      default: false,
    },
    showSearch: { // 是否展示搜索框
      type: Boolean,
      default: false
    },
    visible: {
      type: Boolean,
      default: false
    }
  });

  const show = ref(props.visible);
  watch(() => props.visible, (newVal) => {
    show.value = newVal
  })

  const searchValue = ref('')

  const treeData = ref<any[]>([]);
  /**将树形结构转为平铺数组 */
  const initTreeData = () => {
    if (!props.localdata.length) return;

    treeData.value = tree2list(
      props.localdata,
      "",
      1,
      props.idField,
      props.nameField,
      props.childrenField,
      []
    );

    if (props.debug) {
      console.log("treeData");
      console.table(treeData.value);
    }
  };

  const initTreeSelect = () => {
    if (!treeData.value.length) return;
    if (!props.value.length) return;
    if (props.multiple === false) {
      // 处理单选
      let id = props.value[0];
      let item = treeData.value.find((item) => item.id === id);
      item.checkStatus = 1;
      return;
    }
    // 处理多选
    let handledIds: TreeItemID[] = []; // 已经处理过的id
    for (let i = 0; i < props.value.length; i++) {
      if (handledIds.includes(props.value[i])) {
        // 已经处理过的id 不需要再处理
        continue;
      }
      let index = treeData.value.findIndex(
        (item) => item.id === props.value[i]
      );
      let item = treeData.value[index];
      item.checkStatus = 1
      // 处理后代节点的状态
      let length = treeData.value.length;
      let currentId = item.id;
      for (let i = index + 1; i < length; i++) {
        if (treeData.value[i].path.includes(currentId)) {
          treeData.value[i].checkStatus = item.checkStatus;
          handledIds.push(treeData.value[i].id);
        }

        if (treeData.value[i].level <= item.level) {
          break;
        }
      }
      // 处理前面祖宗的状态
      let currentPath = item.path;
      for (let i = index - 1; i >= 0; i--) {
        if (currentPath.includes(treeData.value[i].id)) {
          let sonNodesCount = treeData.value[i].children?.length || 0;
          if (sonNodesCount) {
            let sonSelectCount = 0;
            let sonIndeterminate = 0;
            for (let j = i + 1; j < length; j++) {
              if (treeData.value[j].level <= treeData.value[i].level) {
                break;
              }
              if (treeData.value[i].children?.includes(treeData.value[j].id)) {
                if (treeData.value[j].checkStatus === 1) {
                  sonSelectCount++;
                } else if (treeData.value[j].checkStatus === 2) {
                  sonIndeterminate++;
                }
              }
            }
            if (sonSelectCount === sonNodesCount) {
              treeData.value[i].checkStatus = 1;
            } else if (sonSelectCount > 0 || sonIndeterminate > 0) {
              treeData.value[i].checkStatus = 2;
            } else {
              treeData.value[i].checkStatus = 0;
            }
          }
          handledIds.push(treeData.value[i].id);
        }
      }
    }
  };

  watch(
    [() => props.localdata, () => props.value],
    (newVal) => {
      // 只有 localdata 有值，才有必要，否则没必要处理树的数据
      initTreeData();
      initTreeSelect();
    },
    { immediate: true }
  );

 
  const open = () => {
    show.value = true;
  };
  const close = () => {
    show.value = false;
    emit("update:visible", false);
  };

  defineExpose({
    open,
    close,
  });

  const clickMask = () => {
    if (!props.maskClose) return;
    close();
  };

  const confirm = () => {
    close();
    let data = treeData.value
      .filter((item) => item.checkStatus === 1)
      .map((item) => item.id);
    nextTick(() => {
      emit("confirm", data);
    });
  };

  const clear = () => {
    treeData.value.forEach((item) => {
      item.checkStatus = 0;
    });
  };

  // --------util---------
  // level 从1开始计数
  // 单独构建一份扁平化数据使用，不修改原始数据
  // 将树结构展平很关键，更新选中状态时，在数组里，a节点的所有祖宗节点都出现在它前面，a节点的所有后代节点都出现在它的后面，这会方便计算
  function tree2list(
    tree = [],
    parentId = "",
    level = 1,
    idField,
    nameField,
    childrenField,
    path = []
  ) {
    let list: any[] = [];
    tree.forEach((item) => {
      let __path = [...path, item[idField]];
      let one = {
        id: item[idField], /**此处的id是内部数据使用，不影响外界原始数据。为了代码整体简单，不再到处使用[idField] */
        name: item[nameField],
        level: level,
        parentId: parentId,
        path: __path,
        isShowSelf: level === 1 ? true : (props.showSearch ? true : props.defaultExpandAll), // 默认展示第一层，其余根据defaultExpandAll判断是否展示
        isShowChild: props.showSearch ? true  : props.defaultExpandAll,
        isLastLevel: !item[childrenField] || !item[childrenField].length,
        checkStatus: 0,
        children: (item[childrenField] ?? []).map(
          (child) => child[idField]
        ) /**记录儿子的id，方便后面计算 */,
      };
      list.push(one);
      if (
        Array.isArray(item[childrenField]) &&
        (item[childrenField] as Array<any>).length > 0
      ) {
        list = list.concat(
          tree2list(
            item[childrenField],
            item[idField],
            level + 1,
            idField,
            nameField,
            childrenField,
            __path
          )
        );
      }
    });
    return list;
  }

  // 展开折叠
  function toggleItem(item, index) {
    if (item.isLastLevel) return;
    item.isShowChild = !item.isShowChild;
    if (item.isShowChild) {
      // 展开，只展开下一级
      for (let i = 0; i < treeData.value.length; i++) {
        if (treeData.value[i].parentId === item.id) {
          treeData.value[i].isShowSelf = true;
        }
      }
    } else {
      // 收起，所有的后台都收起
      for (let i = 0; i < treeData.value.length; i++) {
        let { id, parentId, path } = treeData.value[i];
        if (parentId === item.id) {
          // 直接子级
          treeData.value[i].isShowSelf = false;
          treeData.value[i].isShowChild = false;
        } else if (path.includes(item.id) && id !== item.id) {
          // 后台子级
          treeData.value[i].isShowSelf = false;
          treeData.value[i].isShowChild = false;
        }
      }
    }
  }

  // 0：未选中 1：选中 2：半选
  function toggleSelect(item, index) {
    // 更新自己的状态
    if (item.checkStatus === 0) {
      item.checkStatus = 1;
    } else if (item.checkStatus === 1) {
      item.checkStatus = 0;
    } else if (item.checkStatus === 2) {
      item.checkStatus = 1;
    }

    if (!props.multiple) {
      // 单选
      if (item.checkStatus === 1) {
        for (let i = 0; i < treeData.value.length; i++) {
          if (treeData.value[i].id !== item.id) {
            treeData.value[i].checkStatus = 0;
          }
        }
      }

      return;
    }

    /**
     * 多选
     * 更新所有子级节点的状态
     * a 选中，所有子级都选中
     * a 取消选中，所有子级都取消
     * ====>
     * a 的所有子级状态都跟 a 一样
     */
    handleDescendantStatus(item, index);

    /**
     * 处理a节点的所有祖宗节点，他们都在a的前面
     */
    handleAncestorStatus(item, index);

    if (props.debug) {
      console.log("treeData");
      console.table(treeData.value);
    }
  }

  function handleDescendantStatus(item, index) {
    let length = treeData.value.length;
    let currentId = item.id;
    // tree2list 以后，a节点的所有子级节点都出现在它后面，所以i从index+1开始
    for (let i = index + 1; i < length; i++) {
      // 如果某个节点的path里出现了 currentId，那就说明这个节点是 currentId 的子级
      if (treeData.value[i].path.includes(currentId)) {
        treeData.value[i].checkStatus = item.checkStatus;
      }

      if (treeData.value[i].level <= item.level) {
        // 还可以继续优化，不用遍历到length
        // a 后面的节点，包括它所有的子级，a的兄弟，和其他级别的节点，
        // 当遍历到某个节点的level 跟a 一个级别时，那就可以断掉了
        break;
      }
    }
  }

  function handleAncestorStatus(item, index) {
    let currentPath = item.path;
    let length = treeData.value.length;
    for (let i = index - 1; i >= 0; i--) {
      if (currentPath.includes(treeData.value[i].id)) {
        /**
         * b 节点出现在 a节点的path里，那b就是a的祖宗节点, 那现在要更新b节点的状态了
         * b的所有儿子节点出现在b的后面，
         */
        let sonNodesCount = treeData.value[i].children?.length || 0;
        if (sonNodesCount) {
          let sonSelectCount = 0;
          let sonIndeterminate = 0;
          for (let j = i + 1; j < length; j++) {
            if (treeData.value[j].level <= treeData.value[i].level) {
              break;
            }
            if (treeData.value[i].children?.includes(treeData.value[j].id)) {
              if (treeData.value[j].checkStatus === 1) {
                sonSelectCount++;
              } else if (treeData.value[j].checkStatus === 2) {
                sonIndeterminate++;
              }
            }
          }

          /**
           * if 儿子节点数==选中的儿子节点数，那b就是全选
           * else if 儿子节点数>选中的儿子节点数，那就是半选，或者有儿子节点是半选，那就是半选
           * else 没选中
           */
          if (sonSelectCount === sonNodesCount) {
            treeData.value[i].checkStatus = 1;
          } else if (sonSelectCount > 0 || sonIndeterminate > 0) {
            treeData.value[i].checkStatus = 2;
          } else {
            treeData.value[i].checkStatus = 0;
          }
        }
      }
    }
  }

  function clickName(item, index) {
    if (item.isLastLevel) {
      toggleSelect(item, index);
    } else {
      toggleItem(item, index);
    }
  }

  // --------------------
</script>

<style lang="less">
  @import "./style.less";
</style>
